package vno.sci.mapEditor
{
	import fl.controls.ColorPicker;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flash.utils.ByteArray;
	
	import flashx.textLayout.elements.BreakElement;
	import flashx.textLayout.formats.BackgroundColor;
	
	import hr.binaria.asx3m.Asx3mer;
	
	import mx.styles.AdvancedStyleClient;
	import mx.utils.ColorUtil;
	
	import org.aswing.ASColor;
	import org.aswing.DefaultBoundedRangeModel;
	
	/**
	 * this class is private; 
	 * @author scinart
	 * 
	 */	
	public class Drawer extends Sprite
	{
		private var _mapWidth:int;
		private var _mapHeight:int;
		private var _blockWidth:int;
		private var _blockHeight:int;
		private var _downEvent:MouseEvent;
		private var _upEvent:MouseEvent;
		
		public var shape:String = "Rectangle";
		public var shapes:Array = new Array();
		public var shapesInfo:Array = new Array();
		public var map:Bitmap;
		public var weight:DefaultBoundedRangeModel;
		
		public static var enumColor:Array = new Array(ASColor.WHITE.getRGB(), ASColor.BLUE.getRGB(), ASColor.GREEN.getRGB(), ASColor.YELLOW.getRGB(),ASColor.RED.getRGB(), ASColor.BLACK.getRGB());
		
		
		public function Drawer(width_:int, height_:int, blockWidth_:int, blockHeight_ :int = 0, map_:Bitmap = null, weight_:DefaultBoundedRangeModel = null)
		{
			this._mapWidth= width_;
			this._mapHeight= height_;
			this._blockWidth = blockWidth_;
			this._blockHeight = blockHeight_==0?blockWidth_:blockHeight_;
			this.map = map_;
		    this.weight = weight_;
			
			this.alpha = 0.4;
			
			this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			this.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);

			if (map_ == null)
			{
				this.graphics.beginFill(0);
				this.graphics.drawRect(0,0,width_,height_);
				this.graphics.endFill();
			}
			else
			{
				this.addChild(map_);
			}
			
			this.graphics.lineStyle(1,0xFF0000);
			
			for (var i:int = 0; i < _mapWidth/blockWidth_; i++)
			{
				this.graphics.moveTo(i*blockWidth_,0);
				this.graphics.lineTo(i*blockWidth_,_mapHeight);
			}
			for (var j:int = 0; j < _mapHeight/blockHeight_; j++)
			{
				this.graphics.moveTo(0,j*blockHeight_);
				this.graphics.lineTo(_mapWidth,j*blockHeight_);
			}
		}
		
		private function onMouseWheel(e:MouseEvent):void
		{
			var delta:int = e.delta * 3;
			if (e.shiftKey)
			{
				delta /= 3;
			}
			if (e.altKey)
			{
				delta *= 3;
			}
			if (e.ctrlKey)
			{
				this.x += delta * _blockWidth;
			}
			else
			{
				this.y += delta * _blockHeight;
			}
		}
		private function onMouseUp(e:MouseEvent):void
		{
			if(_downEvent == null)
				return;
			_upEvent = e;
			
			var bx:Number = _downEvent.localX;
			var by:Number = _downEvent.localY;
			var ex:Number = e.localX;
			var ey:Number = e.localY;
			
			if (e.ctrlKey || e.altKey )
			{
				removePixel(e.stageX,e.stageY);
			}
			else if(shape == "Rectangle")
			{
				drawRectangle(bx,by,ex,ey,weight.getValue());
			}
			else if(shape == "Circle")
			{
				drawCircle(bx,by,ex,ey,weight.getValue());
			}
		}
		private function onMouseDown(e:MouseEvent):void
		{
			_downEvent = e;
		}
		private function drawRectangle(beginX_:Number, beginY_:Number, endX_:Number, endY_:Number, weight_:int):void
		{
			
			var bx:int = beginX_/_blockWidth;
			var by:int = beginY_/_blockHeight;
			var ex:int = endX_/_blockWidth;
			var ey:int = endY_/_blockHeight;
			
			var minx:int = Math.min(bx,ex);
			var miny:int = Math.min(by,ey);
			var maxx:int = Math.max(bx,ex);
			var maxy:int = Math.max(by,ey);
			
			var bxn:Number = minx*_blockWidth;
			var byn:Number = miny*_blockHeight;
			var exn:Number = maxx*_blockWidth+_blockWidth-1;
			var eyn:Number = maxy*_blockHeight+_blockHeight-1;
			
			var rect:Shape = new Shape();
			
			rect.graphics.beginFill(enumColor[weight_], 0.7);
			rect.graphics.lineStyle(0);
			rect.graphics.drawRect(bxn,byn,exn-bxn,eyn-byn);
			rect.graphics.endFill();
			
			addShape(rect);
			addInfo("Rectangle",beginX_,beginY_,endX_,endY_,weight_);
		}
		private function drawCircle(beginX_:Number, beginY_:Number, endX_:Number, endY_:Number,weight_:int):void
		{
			var center:Point = new Point(beginX_,beginY_);
			var radius:Number = Point.distance(center, new Point(endX_, endY_));
			var bxn:Number = beginX_-radius;
			var byn:Number = beginY_-radius;
			var exn:Number = beginX_+radius;
			var eyn:Number = beginY_+radius;
			
			var bx:int = bxn/_blockWidth;
			var by:int = byn/_blockHeight;
			var ex:int = exn/_blockWidth
			var ey:int = eyn/_blockHeight;
			
			var rect:Shape = new Shape();
			for (var i:int = bx; i<=ex; i++)
			{
				for (var j:int = by; j<=ey; j++)
				{
					var centralPoint:Point = new Point(i*_blockWidth+_blockWidth/2, j*_blockHeight+_blockHeight/2);
					var distance:Number = Point.distance(centralPoint, center)
					if (distance <= radius)
					{
						var rectbxn:Number = i*_blockWidth;	
						var rectbyn:Number = j*_blockHeight;
						var rectexn:Number = i*_blockWidth+_blockWidth-1;
						var recteyn:Number = j*_blockHeight+_blockHeight-1;

						rect.graphics.beginFill(enumColor[weight_] ,0.7);
						rect.graphics.lineStyle(0);
						rect.graphics.drawRect(rectbxn,rectbyn,rectexn-rectbxn,recteyn-rectbyn);
						rect.graphics.endFill();
					}
				}
			}
			addShape(rect);
			addInfo("Circle",beginX_,beginY_,endX_,endY_,weight_);
			
		}
		public function removePixel(beginX_:Number, beginY_:Number):void
		{
			for (var i:int = shapes.length-1; i>=0; i--)
			{
				if ((shapes[i] as Shape).hitTestPoint(beginX_,beginY_))
				{
					this.removeChild(shapes[i]);
					shapes.splice(i,1);
					shapesInfo.splice(i,1);
					break;
				}
			}
		}
		private function addShape(s:Shape):void
		{
			this.addChild(s);
			shapes.push(s);
		}
		private function addInfo(...params):void
		{
			shapesInfo.push(params);
		}
		public function undo():void
		{
			if (shapes.length != 0)
			{
				this.removeChild(shapes.pop());
				shapesInfo.pop();
			}
		}
		public function load(ba:ByteArray):void
		{
			_mapWidth = ba.readInt();
			_mapHeight = ba.readInt();
			_blockWidth = ba.readInt();
			_blockHeight = ba.readInt();
			var shapesInfoCopy:Array =Asx3mer.instance.fromXML(ba.readObject() as XML) as Array;

			
			//clear old shape;
			for (var iao:int = 0; iao < shapes.length; iao++)
			{
				this.removeChild(shapes[iao]);
			}
			shapes = new Array();
			
			//clear old shapeInfo;
			this.shapesInfo = new Array();
			
			// add new shapes
			for (var i:int = 0; i < shapesInfoCopy.length; i++)
			{
				var item:Array = shapesInfoCopy[i];
				if (item[0] == "Rectangle")
				{
					this.drawRectangle(item[1], item[2], item[3], item[4], item[5]);
				}
				else if (item[0] == "Circle")
				{
					this.drawCircle(item[1], item[2], item[3], item[4], item[5]);
				}
			}
		}
		public function save():ByteArray
		{
			var ba:ByteArray = new ByteArray();
			ba.writeInt(_mapWidth);
			ba.writeInt(_mapHeight);
			ba.writeInt(_blockWidth);
			ba.writeInt(_blockHeight);
			
			//convert object to XML
			var xmlObj:XML=Asx3mer.instance.toXML(shapesInfo);
//			trace(xmlObj);  
			
			ba.writeObject(xmlObj);
			return ba;
		}
		public function binarySave():ByteArray
		{
			var ba:ByteArray = new ByteArray();

//			var shapesInfoCopy:Array = new Array();
//			for (var i:int = 0; i < shapesInfo.length; i++)
//			{
//				var item = shapesInfo[i];
//				if (item[0] == "Rectangle")
//				{
//					shapesInfoCopy.push(item);
//				}
//				else if (item[0] == "Circle")
//				{
//					var x:int = item[1]/blockWidth;
//					var y:int = item[2]/blockHeight;
//					shapesInfoCopy.push("Circle",x*blockWidth,y*blockHeight, Point.distance(new Point(item[1],item[2]), new Point(item[3], item[4])), item[5]);
//				}
//			}
			ba.writeShort(shapesInfo.length);
			ba.writeShort(_mapWidth);
			ba.writeShort(_mapHeight);
			ba.writeShort(_blockWidth);
			ba.writeShort(_blockHeight);
			for (var i:int = 0; i < shapesInfo.length; i++)
			{
				var item:Array = shapesInfo[i];
				if (item[0] == "Rectangle")
				{
					ba.writeUTFBytes("\0R");
				}
				else if (item[0] == "Circle")
				{
					ba.writeUTFBytes("\0C");
				}
				ba.writeShort(item[1]);
				ba.writeShort(item[2]);
				ba.writeShort(item[3]);
				ba.writeShort(item[4]);
				ba.writeShort(item[5]);
				
			}
			return ba;
		}
		public function binaryLoad(ba:ByteArray):void
		{
			var length:int = ba.readUnsignedShort();
			_mapWidth = ba.readUnsignedShort();
			_mapHeight = ba.readUnsignedShort();
			_blockWidth = ba.readUnsignedShort();
			_blockHeight = ba.readUnsignedShort();	
			
			//clear old shape;
			for (var i:int = 0; i < shapes.length; i++)
			{
				this.removeChild(shapes[i]);
			}
			shapes = new Array();
			
			//clear old shapeInfo;
			this.shapesInfo = new Array();
			
			// add new shapes
			for (var ii:int = 0; ii < length; ii++)
			{
				var m:String = ba.readUTFBytes(2);
				if (m == "\0R")
				{
					this.drawRectangle(ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort());
				}
				else if (m == "\0C")
				{
					this.drawCircle(ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort(),ba.readUnsignedShort());
				}
			}
		}
		
		/**
		 * @param ba binarySave产生的ByteArray
		 * @return 二维int数组，列主序
		 * 
		 */		
		public static function toArray(ba:ByteArray):Vector.<Vector.<int>>
		{
			ba.position = 0;
			var length:int = ba.readUnsignedShort();
			var mapWidth:int = ba.readUnsignedShort();
			var mapHeight:int = ba.readUnsignedShort();
			var blockWidth:int = ba.readUnsignedShort();
			var blockHeight:int = ba.readUnsignedShort();
			
			var mW:int = mapWidth/blockWidth;
			var mH:int = mapHeight/blockHeight;			
//			var array:Array = new Array(mapHeight/blockHeight);
			var array:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(mW,true);

			for (var a:int = 0; a < array.length; a++)
			{
				array[a] = new Vector.<int>(mH,true);
			}
			
//			for (var a:int = 0; a < array.length; a++)
//			{
//				array[a] = new Array(mapWidth/blockWidth);
//			}
			
//			new version default is 0;
//			for (var ai:int = 0; ai < array.length; ai++)
//			{
//				for (var aj:int = 0; aj < mapWidth/blockWidth; aj++)
//				{
//					array[ai][aj] = 0;
//				}
//			}
			
			for (var ixx:int = 0; ixx < length; ixx++)
			{
				var m:String = ba.readUTFBytes(2);
				
				var beginX:int = ba.readUnsignedShort();
				var beginY:int = ba.readUnsignedShort();
				var endX:int = ba.readUnsignedShort();
				var endY:int = ba.readUnsignedShort();
				var weight:int = ba.readUnsignedShort();

				var bx:int = beginX/blockWidth;
				var by:int = beginY/blockHeight;
				var ex:int = endX/blockWidth;
				var ey:int = endY/blockHeight;
				
				if (m == "\0R")
				{
					var minx:int = Math.min(bx,ex);
					var miny:int = Math.min(by,ey);
					var maxx:int = Math.max(bx,ex);
					var maxy:int = Math.max(by,ey);
					
					for (var j:int = minx; j <= maxx; j++)
					{
						for (var k:int = miny; k <= maxy; k++)
						{
							if(k>=0 && k <=mapHeight/blockHeight &&
							   j>=0 && j <= mapWidth/blockWidth)
							{
//								array[k][j] = weight;
								
//								new version; column major order.
								array[j][k] = weight;
							}
						}
					}
					
				}
				else if (m == "\0C")
				{
					var center:Point = new Point(beginX,beginY);
					var radius:Number = Point.distance(center, new Point(endX, endY));
					
					
					var bxn:Number = beginX-radius;
					var byn:Number = beginY-radius;
					var exn:Number = beginX+radius;
					var eyn:Number = beginY+radius;
					
					var cbx:int = bxn/blockWidth;
					var cby:int = byn/blockHeight;
					var cex:int = exn/blockWidth
					var cey:int = eyn/blockHeight;
					
					for (var mm:int = cbx; mm<=cex; mm++)
					{	1
						for (var nn:int = cby; nn <= cey; nn++)
						{
							var centralPoint:Point = new Point(mm*blockWidth+blockWidth/2, nn*blockHeight+blockHeight/2);
							var distance:Number = Point.distance(centralPoint, center)
							if (distance <= radius &&
								mm>=0 && nn <mapHeight/blockHeight &&
									nn >= 0 && mm < mapWidth/blockWidth)
							{
//								array[nn][mm] = weight;
								
//								new version: column major order.
								array[mm][nn] = weight;
							}
						}
					}
				}
			}
			return array;
		}
	}
}